package com.gitee.wsl.compose.modifier.draw

import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.gitee.wsl.common.ui.ext.drawScope.drawBlockWithCheckerAndLayer
import com.gitee.wsl.compose.base.AspectRatio
import com.gitee.wsl.compose.modifier.ModifierDrawScope


/**
 * Draws [shape] as [Outline] with a checker background by clipping image with [shape] using
 * [BlendMode.SrcIn]. Shape contains image while background is checker
 */
internal fun Modifier.drawOutlineWithBlendModeAndChecker(
    aspectRatio: AspectRatio,
    shape: Shape,
    density: Density,
    dstBitmap: ImageBitmap,
    coefficient: Float = .9f,
    color: Color = Color.Red,
) = this.then(
    Modifier.drawWithCache {

        val (offset, outline) = buildOutline(
            aspectRatio,
            coefficient,
            shape,
            size,
            layoutDirection,
            density
        )

        onDrawWithContent {
            drawBlockWithCheckerAndLayer(dstBitmap) {
                translate(left = offset.x, top = offset.y) {
                    drawOutline(
                        outline = outline,
                        color = color,
                    )
                }
            }
        }
    }
)

fun ModifierDrawScope.drawOutlineWithBlendModeAndChecker(
    aspectRatio: AspectRatio,
    shape: Shape,
    density: Density,
    dstBitmap: ImageBitmap,
    coefficient: Float = .9f,
    color: Color = Color.Red,
){
    modifier = modifier.drawOutlineWithBlendModeAndChecker(aspectRatio, shape, density, dstBitmap, coefficient, color)
}


/**
 * Modify element to add checker with appearance specified with a [shape], and [size]
 * and clip it.
 * @param size size of the checker. If size is [DpSize.Unspecified] **10.dp** is used by
 * default.
 * @param shape shape of the checker.
 */
internal fun Modifier.drawChecker(shape: Shape, size: DpSize = DpSize.Unspecified) = this
    .clip(shape)
    .then(
        drawWithCache {
            this.onDrawBehind {
                val width = this.size.width
                val height = this.size.height

                val checkerWidth =
                    (if (size != DpSize.Unspecified) size.width.toPx() else 10.dp.toPx())
                        .coerceAtMost(width / 2)

                val checkerHeight =
                    (if (size != DpSize.Unspecified) size.height.toPx() else 10.dp.toPx())
                        .coerceAtMost(height / 2)

                val horizontalSteps = (width / checkerWidth).toInt()
                val verticalSteps = (height / checkerHeight).toInt()

                for (y in 0..verticalSteps) {
                    for (x in 0..horizontalSteps) {
                        val isGrayTile = ((x + y) % 2 == 1)
                        drawRect(
                            color = if (isGrayTile) Color.LightGray else Color.White,
                            topLeft = Offset(x * checkerWidth, y * checkerHeight),
                            size = Size(checkerWidth, checkerHeight)
                        )
                    }
                }
            }
        }
    )


fun ModifierDrawScope.drawChecker(shape: Shape, size: DpSize = DpSize.Unspecified){
    modifier = modifier.drawChecker(shape, size)
}

/**
 * Build an outline from a shape using aspect ratio, shape and coefficient to scale
 *
 * @return [Triple] that contains left, top offset and [Outline]
 */
fun buildOutline(
    aspectRatio: AspectRatio,
    coefficient: Float,
    shape: Shape,
    size: Size,
    layoutDirection: LayoutDirection,
    density: Density
): Pair<Offset, Outline> {

    val (shapeSize, offset) = calculateSizeAndOffsetFromAspectRatio(aspectRatio, coefficient, size)

    val outline = shape.createOutline(
        size = shapeSize,
        layoutDirection = layoutDirection,
        density = density
    )
    return Pair(offset, outline)
}


/**
 * Calculate new size and offset based on [size], [coefficient] and [aspectRatio]
 *
 * For 4/3f aspect ratio with 1000px width, 1000px height with coefficient 1f
 * it returns Size(1000f, 750f), Offset(0f, 125f).
 */
fun calculateSizeAndOffsetFromAspectRatio(
    aspectRatio: AspectRatio,
    coefficient: Float,
    size: Size,
): Pair<Size, Offset> {
    val width = size.width
    val height = size.height

    val value = aspectRatio.value

    val newSize = if (aspectRatio == AspectRatio.Original) {
        Size(width * coefficient, height * coefficient)
    } else if (value > 1) {
        Size(
            width = coefficient * width,
            height = coefficient * width / value
        )
    } else {
        Size(width = coefficient * height * value, height = coefficient * height)
    }

    val left = (width - newSize.width) / 2
    val top = (height - newSize.height) / 2

    return Pair(newSize, Offset(left, top))
}